package com.tim.arc.bytebuddy;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.utility.JavaModule;

import java.io.IOException;

/**
 *ByteBuddy 常用来进行动态增强的操作有三种：
 * redefine。修改已有类
 * subclass。生成子类
 * rebase。生成子类，增加后缀保留原字段、方法
 *
 * */
public class ByteBuddyExecute {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
//        subclass01();
//        subclass02();
//        redefine01();
        redefine02();
    }

    /**创建子类；过滤toString方法；重写方法*/
    public static void subclass01() throws InstantiationException, IllegalAccessException {
        Class<?> loaded = new ByteBuddy()
                .subclass(Object.class)
                .name("Overwrite01")
                .method(ElementMatchers.hasMethodName("toString"))
                .intercept(FixedValue.value("Hello bytebuddy"))
                .make()
                .load(Thread.currentThread().getContextClassLoader())
                .getLoaded();
        Object object = loaded.newInstance();
        System.out.println(object);

    }

    /**创建子类；过滤toString方法；重写方法*/
    public static void subclass02() throws InstantiationException, IllegalAccessException {
        Class<?> loaded = new ByteBuddy()
                .subclass(Object.class)
                .name("Overwrite02")
                .method(ElementMatchers.hasMethodName("toString"))
                // 方法名、or参数列表唯一匹配
                .intercept(MethodDelegation.to(ToString.class))
                .make()
                .load(Thread.currentThread().getContextClassLoader())
                .getLoaded();
        Object object = loaded.newInstance();
        System.out.println(object);

    }

    public static void redefine01() {
        ByteBuddyAgent.install();

        new ByteBuddy()
                .redefine(Foo.class)
                .visit(Advice.to(FooToStringRedefine.class).on(ElementMatchers.hasMethodName("toString")))
                .make()
                .load(Thread.currentThread().getContextClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
        System.out.println(new Foo().toString());

    }

    public static void redefine02() {
        ByteBuddyAgent.install();
        ByteBuddy byteBuddy = new ByteBuddy();
        AgentBuilder agentBuilder = new AgentBuilder.Default(byteBuddy)
                .with(AgentBuilder.TypeStrategy.Default.REDEFINE)
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
                .with(new AgentBuilder.Listener.StreamWriting(System.out));
        agentBuilder.type(ElementMatchers.named("toString").and(ElementMatchers.anyOf(Foo.class)))
                        .transform(new AgentBuilder.Transformer() {
                            @Override
                            public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
                                builder.method(ElementMatchers.named("toString"))
                                        .intercept(MethodDelegation.to(ToString.class));
                                return builder;
                            }
                        })
                .installOnByteBuddyAgent();
        System.out.println(new Foo().toString());


    }



}
